package com.maxmlr.framework.context;

import com.google.common.collect.Maps;
import com.maxmlr.framework.annotation.Autowired;
import com.maxmlr.framework.annotation.Controller;
import com.maxmlr.framework.annotation.Service;
import com.maxmlr.framework.annotation.Value;
import com.maxmlr.framework.aop.AopConfig;
import com.maxmlr.framework.beans.BeanDefinition;
import com.maxmlr.framework.beans.BeanPostProcessor;
import com.maxmlr.framework.beans.BeanWrapper;
import com.maxmlr.framework.core.BeanFactory;
import lombok.Data;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName DefaultListableBeanFactory
 * @Description TODO
 * @Author maxiao
 * @Date 2020/3/13 下午4:13
 * @Version V1.0
 */
@Data
public class DefaultListableBeanFactory implements BeanFactory {

    //ioc 核心容器
    private final Map<String, BeanDefinition> beanDefinitionMap = Maps.newConcurrentMap();

    //用来存储所有的被代理过的对象
    private Map<String, BeanWrapper> beanWrapperMap = Maps.newConcurrentMap();

    //实例化对象
    private final Map<String, Object> beansMap = Maps.newConcurrentMap();

    private Properties properties;

    //注册初始化ioc容器
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws Exception {
        synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
    }

    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        String beanClassName = beanDefinition.getBeanClassName();

        try {

            Object bean = instantionBean(beanDefinition);

            BeanPostProcessor beanPostProcessor = new BeanPostProcessor();
            //在实例初始化以前调用一次
            beanPostProcessor.postProcessBeforeInitialization(bean, beanName);

            BeanWrapper beanWrapper = new BeanWrapper(bean);
            beanWrapper.setAopConfig(instantionAopConfig(bean));
            beanWrapper.setBeanPostProcessor(beanPostProcessor);
            this.beanWrapperMap.put(beanName, beanWrapper);

            //在实例初始化以后调用一次
            beanPostProcessor.postProcessAfterInitialization(bean, beanName);

            //此时返回的都是原生对象
            return this.beanWrapperMap.get(beanName).getOriginalInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析配置 aop 信息，存入aop配置中
     *
     * @param bean
     * @return
     * @throws Exception
     */
    private AopConfig instantionAopConfig(Object bean) throws Exception {
        AopConfig aopConfig = new AopConfig();
        String expression = properties.getProperty("pointCut");
        String[] before = properties.getProperty("aspectBefore").split("\\s");
        String[] after = properties.getProperty("aspectAfter").split("\\s");

        Pattern compile = Pattern.compile(expression);

        Class aspectClass = Class.forName(before[0]);

        Method[] methods = bean.getClass().getMethods();
        for (Method m : methods) {
            Matcher matcher = compile.matcher(m.toString());
            if (matcher.matches()) {
                Method[] ms = new Method[2];
                ms[0] = aspectClass.getMethod(before[1]);
                ms[1] = aspectClass.getMethod(after[1]);
                aopConfig.put(m, aspectClass.newInstance(), ms);
            }
        }

        return aopConfig;
    }


    private Object instantionBean(BeanDefinition beanDefinition) {
        String beanClassName = beanDefinition.getBeanClassName();
        try {
            Object instance = null;
            if (this.beansMap.containsKey(beanClassName)) {
                return this.beansMap.get(beanClassName);
            } else {
                Class<?> clazz = Class.forName(beanClassName);
                instance = clazz.newInstance();
                this.beansMap.put(beanClassName, instance);
            }
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public void doAutowrited() throws Exception {
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();

            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                Object obj = getBean(beanName);
//                System.out.println(obj.getClass());
            }

        }

        for (Map.Entry<String, BeanWrapper> beanWrapperEntry : this.beanWrapperMap.entrySet()) {
            String beanName = beanWrapperEntry.getKey();
            BeanWrapper beanWrapper = beanWrapperEntry.getValue();
            Object wrapperInstance = beanWrapper.getWrapperInstance();
            Object originalInstance = beanWrapper.getOriginalInstance();
            Class clazz = originalInstance.getClass();

            if (!(clazz.isAnnotationPresent(Service.class)
                    || clazz.isAnnotationPresent(Controller.class))) {
                continue;
            }

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    initFieldAutowired(originalInstance, field);
                } else if (field.isAnnotationPresent(Value.class)) {
                    initFieldValue(originalInstance, field);
                }
            }

        }
    }

    private void initFieldValue(Object value, Field field) {
        Value annotation = field.getAnnotation(Value.class);
        String name = annotation.value().trim();
        if ("".equals(name)) {
            name = field.getName();
        }
        String property = this.properties.getProperty(name);
        if (null == property) {
            return;
        }
        field.setAccessible(true);
        try {
            System.out.println(field.getType() + "---" + property);
            if (String.class == field.getType()) {
                field.set(value, property);
            } else if (Integer.class == field.getType()) {
                field.set(value, Integer.valueOf(property));
            } else {
                //留着
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注入必需用代理对象
     *
     * @param value
     * @param field
     * @throws Exception
     */
    private void initFieldAutowired(Object value, Field field) throws Exception {
        Autowired annotation = field.getAnnotation(Autowired.class);
        String name = annotation.value().trim();
        if ("".equals(name)) {
            name = field.getType().getName();
        }
        field.setAccessible(true);
        try {
            //注入要用代理对象
            Object instance = this.beanWrapperMap.get(name).getWrapperInstance();
            field.set(value, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
